Handoff Contacts — Collaborator Links & per-handoff Grants Spec update: QR is now only for establishing a first-time Collaborator Link — a long-lived E2E trust binding that proves identity and gives a delivery address, and grants zero session access.
Starting a handoff = picking a contact; the daemon delivers the offer over the existing link plus a content-free push. Per-handoff access is a separate temporary Grant (contact + handoff + session + scope + expiry).
Links are one-directional (A→B) or mutual (⇄). “Connected” means the trust binding is valid — never presence. Status colour law unchanged: waiting = neutral · in-progress = terracotta · returned/completed = green · recalled/expired = amber · declined/cancelled = grey · red reserved for Recall, Revoke & Remove.
Frame 1 · Recipient picker390×844 · dark · draft sheet, page 2 — replaces the free-text recipient row

Choose recipient

Search collaborators…
Recent
FFrank Osei2 handoffs · last week
MMei Tanaka5 handoffs · 3 days ago
All contacts5
AAiko Satoconnected 12d agono daemon
DDev Raoconnected 2mo ago · 1 handoff
FFrank Oseiconnected 6w ago · 2 handoffs
MMei Tanakaconnected 4mo ago · 5 handoffs
SSofia Lindqvistconnected 5mo agono daemon
Connect a new colleague…
one-time QR · establishes trust only
Tapping the Recipient row pushes this page inside the same sheet — back
chevron, no new modal. Tap a row = select and pop straight back; there is
no confirm step, so rows are 60pt and the chevron is decoration.
Direction glyphs are muted mono, never accent: mutual, you can send
to them only. no daemon is a quiet fact, not a warning — they can still
receive and review a handoff from a phone.
The connect row sits in the footer, separated by the hairline: it is the
only surface in the whole flow that still shows a QR.
Frame 1b · Picker — no contacts yet390×520 · dark · first-run empty

Choose recipient

You haven't connected anyone yet. Connecting is a one-time QR scan that proves who they are — it shares no sessions.

Empty state carries the whole mental model in one sentence, then a single
primary. No secondary “skip” — there is no handoff without a contact.
Frame 2 · Draft sheet — contact selected390×1210 scrolling · dark · delta vs shipped draft = recipient row + footer verb

Hand off this session

Refactor auth moduleClaude
~/proj/app/cc-pocket·feat/auth-refactor
Rolecontinue arrives later
Reviewread-only
Continuecan edit · later
Recipient
FFrank OseiChange
Offer expiresaccess, not the link
1h4h24h
THEY WILL SEE
  • The full session transcriptevery message, tool call and diff
  • Code in the shared roots~/proj/app/cc-pocket
    ~/proj/app/cc-pocket-protocol
  • Read-only tools & diffsRead · Grep · Glob · git diff
THEY CAN'T DO
  • Edit or write files
  • Reach your other sessions or folders
  • Pass this handoff on to someone else
The full conversation history of this session will be visible — including anything you pasted into it.
Handoff briefautoEdit
Goal

Split AuthInterceptor so token refresh is testable without a live socket.

Ask

Review the concurrency around the refresh mutex before I open the PR.

Nothing else moved: session card, Role segment, expiry pills, boundary
card and brief are the shipped components, unchanged. Two deltas only —
the recipient field now holds a contact chip (28pt avatar + label + direction
glyph, whole chip tappable, Change re-opens Frame 1), and the primary
reads Send to Frank: the offer is delivered over the existing link, so
there is no artefact for the user to carry anywhere.
Expiry now labels the Grant, not the link — hence the hint. The link outlives
every handoff; the grant dies on return, decline or timeout.
Frame 3 · Connect a colleague — initiator390×800 · dark · the only QR left in the product
9:41
K4TP-9BRX
ticket valid 10:00
This QR only establishes a trusted connection. It shares no sessions, folders or code.
waiting for scan…
A collaborator ticket, not an invite: it carries a public key and a delivery
address, no session id and no scope. Short code is the fallback for
anyone who can't scan — same 8-char alphabet as the shipped invite.
“waiting for scan…” uses the neutral dot; nothing is running, and it must
not read as presence. Ticket TTL is short (10 min) because it is a
one-shot introduction, unlike the link it creates.
Frame 3b · Connected — return to the draft390×520 · dark · success sub-state of Frame 3
9:41
A
Aiko SatoConnected
Aiko can now receive handoffs from you. They still see nothing until you send one and they accept it.
“Connected” is green because the binding succeeded — it is a completion,
not a presence light. The primary returns to the interrupted draft with
Aiko pre-selected; Done exists for people who came from Settings.
Frame 4 · Confirm connection — recipient390×844 · dark · trust screen, same seriousness as accept
9:41
PPanda · MacBook Pro
first connection·scanned just now
Safety fingerprint
tiger — brick — mango — void
amber — pilot — nine — harbor

Compare these words with Panda in person or on a call. If they don't match, cancel — someone else may be in the middle.

Panda → youPanda can send you handoffs. You can't see anything of theirs until you accept one.
No access is granted nowno sessions · no folders · no code
The fingerprint is the whole point of the screen, so it gets the largest
type on it (16px mono, two lines of four words) on a raised card. Word
groups beat hex: people read them aloud correctly.
Direction is stated in the second person and includes the negative half —
the recipient's first question is always “what can they see of mine?”.
Cancel is a plain secondary; refusing a connection is not dangerous.
Hit targets: both footer buttons 48pt, spaced 8pt.
Frame 5 · Settings ▸ Collaborators390×760 · dark · a management surface, not a feed
9:41
Search collaborators…
Connected4
FFrank Oseiconnected 12d ago · 3 handoffs
Remove
MMei Tanakaconnected 4mo ago · 5 handoffs
AAiko Satoconnected 2m ago · no handoffs yetno daemon
DDev Raoconnected 2mo ago · 1 handoff
Removed
SSofia Lindqvistlink revoked 8d ago · 2 past handoffsremoved
Reached from Settings; the header QR glyph is the second (and last) entry
to Frame 3. Rows carry facts only — when the link was made and how
many handoffs have run over it. No avatars-with-status, no “last seen”:
we don't have presence and won't imply it.
Swipe reveals a single Remove action — the only danger colour on the
screen. Removed contacts fall to a terminal group at 45%: past handoffs
still reference them, so the row can't just disappear.
Frame 5b · Collaborators — empty390×340 · dark

No collaborators yet. A connection is a one-time QR scan; it proves who someone is and gives your daemon a way to reach them.

Same empty grammar as the shipped Handoffs empty state — glyph,
one sentence, ghost button.
Frame 6 · Contact detail390×1000 scrolling · dark · viewer has no daemon of their own
9:41
P
Panda
MacBook Pro · connected 12d ago
Safety fingerprint
tiger — brick — mango — void
amber — pilot — nine — harbor
Direction
Panda → youPanda can send you handoffs.
You can't send them one.
Complete the reverse linkneeds a daemon on your computer
Handoffs with Panda3
Refactor auth module18 min · 5 findings · todayCompleted
Fix flaky socket testnever opened · last weekExpired
Release notes 1.63 weeks agoDeclined
Remove connectionPanda can no longer reach you; past handoffs stay in history.
Shown from the recipient's side, where the link is one-directional and the
reverse leg is disabled with a reason rather than hidden — the mono hint
tells them exactly what's missing.
History reuses the shipped status chips verbatim, so the colour law reads
identically here and in Session info.
Remove is the last row, states its consequence in one line, and is the only
red on the screen. No confirmation dialog is drawn here — it exists,
and it repeats this same sentence.
Frame 7a · Offer on the home surface390×844 · dark · content-free push, then the card
9:41
cc-pocket
You have a new handoff offer.
Open the app to see the details.
now
PPanda asked you to review a session
Reviewread-onlycc-pocket
expires in 23:57:04
Earlier
MMei · Fix flaky socket testyesterday · you approvedCompleted
DDev · Gateway model presets3 days agoDeclined
The push is deliberately content-free — “you have a new handoff offer”,
no project name, no sender detail — because notification payloads leave
the E2E envelope. Everything specific arrives over the link and renders
in-app.
Two pending offers stack (the peek behind); three or more collapse into
a counted group. View is terracotta: this is a needs-you moment. It opens
the shipped accept preview unchanged — the card is a doorway, not a
second trust screen, so it never lists scope details.
Frame 7b · Notification-tap landing390×620 · dark · same card, focused containment
9:41
PPanda asked you to review a session
Reviewread-onlycc-pocket
expires in 23:57:04
Viewing shows you what Panda is sharing. Nothing opens until you accept on the next screen.
Tapping the push lands on the offer, never straight in the session —
the accept preview must always be crossed on purpose. Same card,
bigger buttons, plus one line explaining that View is safe.
Frame 8 · WAITING banner — delta390×272 · dark · banner region only; rest of the screen unchanged
Offer sent to Frank · Review · expires in 24h
Waiting for Frank to accept
offer sent · Frank notified · expires 23:57:04
Input locked during handoff
Same card, same neutral dot, same 44pt buttons — only the strings and
the left action change. The mono line drops the short code (there is no
code any more) and states delivery instead: offer sent · Frank notified.
“Notified” is honest about what we know — the push was accepted by
the service; it does not claim Frank saw it, and never claims he is online.
Resend notify re-pings over the same link and rate-limits to once a
minute; it does not mint anything new. Recall keeps its danger tint and
stays the rightmost, destructive option.
Desktop — 1200ptSame tokens, same status law. The picker becomes an anchored popover; Collaborators becomes a two-column settings pane.
Frame 9a · Desktop draft modal — contact dropdown1200×800 · dark · popover anchored to the Recipient field
Panda · MacBook Pro
Refactor auth module~/proj/app/cc-pocket
Fix flaky socket test~/proj/app/cc-pocket-protocol
Release notes 1.6~/proj/app/cc-pocket
Refactor auth modulefeat/auth-refactordefault
YOU
add a unit test for the stream parser
Bashgradle :protocol:test42 passed
Message Claude…

Hand off this session

Refactor auth moduleClaude
~/proj/app/cc-pocket·feat/auth-refactor
Rolecontinue arrives later
Reviewread-only
Continuecan edit · later
Recipient
fra
Recent
FFrank Osei2 handoffs · last week
MMei Tanaka5 handoffs · 3 days ago
All contacts
AAiko Satoconnected 12d agono daemon
DDev Raoconnected 2mo ago
Connect a new colleague…
Offer expiresaccess, not the link
1h4h24h
THEY WILL SEE
  • The full session transcriptevery message, tool call and diff
  • Code in the shared roots~/proj/app/cc-pocket
    ~/proj/app/cc-pocket-protocol
THEY CAN'T DO
  • Edit or write files
  • Reach your other sessions or folders
  • Pass this handoff on to someone else
The full conversation history of this session will be visible — including anything you pasted into it.
Handoff briefautoEdit
Ask

Review the concurrency around the refresh mutex before I open the PR.

Your input locks until Frank returns
The picker is a type-ahead popover anchored to the field, matching the model popover:
340px, raised, hairline, same group labels as mobile. Typing filters; ↑↓ moves; ⏎ selects.
The connect row is pinned to the popover footer so the QR path is reachable without
abandoning the draft — choosing it opens Frame 3 as a small dialog and returns here.
Desktop footer keeps the shipped cost-restatement chip; only the primary verb changed.
Frame 9b · Desktop Settings ▸ Collaborators1200×800 · dark · list + detail, existing two-column settings grammar
Settings
Settings
Daemon & devices
Shared folders
Collaborators
Handoffs
Advanced
Collaborators4 connected
Search…
FFrank Oseiconnected 12d ago · 3 handoffs
MMei Tanakaconnected 4mo ago · 5 handoffs
AAiko Satoconnected 2m ago · no handoffs yetno daemon
DDev Raoconnected 2mo ago · 1 handoff
SSofia Lindqvistlink revoked 8d agoremoved
F
Frank Osei
Frank's Mac mini · connected 12d ago
Safety fingerprint
tiger — brick — mango — void
amber — pilot — nine — harbor
Direction
Mutualyou can send Frank handoffs · Frank can send you handoffsBoth ways
Handoffs with Frank3
Refactor auth module18 min · 5 findings · todayCompleted
Fix flaky socket testpulled back after 4 min · 2d agoRecalled
Release notes 1.63 weeks agoDeclined
Remove connectionFrank can no longer reach you and you can't send him handoffs; past handoffs stay in history.
List left, detail right — the same 340px + fluid split the draft modal uses, so Settings and
dialogs share one rhythm. Selection is a raised row, not a tinted one: terracotta stays
reserved for needs-you moments.
Mutual links collapse to a single row with a Both ways chip rather than two entries;
the pane never implies presence — “connected 12d ago” is when the binding was made.
Remove gets an explicit button on desktop (no swipe affordance) and states both halves of
the consequence for a mutual link.